﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using Borg.AttributeAnalyze;
using Borg.AttributeAnalyze.Result;
using Properties;

namespace Borg
{
    public class ExtensionTypeInfo : RepositoryMetaElement
    {
        internal static int nextId = 1;

        internal static QualificationState unqualifiedState =
            QualificationState.QualificationMissing |
            QualificationState.ExtensionNoDefaultConstructor;

        internal static QualificationState warningState =
            QualificationState.ExtensionNameAlreadyExists |
            QualificationState.ExtensionHasNoPlugs;

        internal static QualificationState methodWarningState =
            QualificationState.ExtensionOnCreatedMethodNotFound |
            QualificationState.ExtensionOnReleasingMethodNotFound |
            QualificationState.ExtensionOnReleasedMethodNotFound;

        internal bool eventHandlerMethodInfosSet = false;

        internal MethodInfo MiOnCreated = null;
        internal MethodInfo MiOnReleasing = null;
        internal MethodInfo MiOnReleased = null;

        internal PluginInfo pluginInfo;
        internal readonly ExtensionAttribute extensionAttr;

        internal readonly string name;
        internal Type type;
        internal string typeName;

        internal readonly RepositoryCollection<ExtensionInfo> extensionInfos;
        internal ExtensionInfo sharedExtension;

        internal RepositoryCollection<PlugTypeInfo> plugTypeInfos;
        internal RepositoryCollection<SlotTypeInfo> slotTypeInfos;
        internal RepositoryCollection<MonitorInfo> monitorInfos;

        internal readonly ExtensionTypeInfo.CreateExtensionDelegate createExtensionHandle;

        public event CancelExtensionEventHandler ExtensionCreating;
        public event ExtensionEventHandler ExtensionCreated;
        public event CancelExtensionEventHandler ExtensionReleasing;
        public event ExtensionEventHandler ExtensionReleased;

        public override string Name
        {
            get { return name; }
        }

        public override Assembly Assembly
        {
            get { return pluginInfo.assembly; }
        }

        public virtual PluginInfo PluginInfo
        {
            get { return pluginInfo; }
        }

        public virtual RepositoryCollection<PlugTypeInfo> PlugTypeInfos
        {
            get { return plugTypeInfos; }
        }

        public virtual RepositoryCollection<SlotTypeInfo> SlotTypeInfos
        {
            get { return slotTypeInfos; }
        }

        public virtual RepositoryCollection<ExtensionInfo> ExtensionInfos
        {
            get { return extensionInfos; }
        }

        public virtual RepositoryCollection<MonitorInfo> MonitorInfos
        {
            get { return monitorInfos; }
        }

        public bool Singleton
        {
            get { return extensionAttr.Singleton; }
            internal set
            {
                if (Singleton == value)
                    return;

                extensionAttr.Singleton = value;
                OnPropertyChanged("Singleton");
            }
        }

        public bool AutoRelease
        {
            get { return extensionAttr.AutoRelease; }
            set
            {
                if (!Runtime.IsCompositionAllowed)
                    throw new InvalidOperationException("Composition not allowed");

                Runtime.Composer.SetAutoRelease(this, value);
            }
        }

        public override bool IsQualified
        {
            get { return (qualificationState & unqualifiedState) == QualificationState.None; }
        }

        public Type Type
        {
            get { return type; }
        }

        public string TypeName
        {
            get { return typeName; }
        }

        public IEnumerable<KeyValuePair<string, string>> EventHandlers
        {
            get
            {
                yield return new KeyValuePair<string, string>("OnCreated", extensionAttr.OnCreated);
                yield return new KeyValuePair<string, string>("OnReleasing", extensionAttr.OnReleasing);
                yield return new KeyValuePair<string, string>("OnReleased", extensionAttr.OnReleased);
            }
        }

        static ExtensionTypeInfo()
        { }

        protected internal ExtensionTypeInfo(PluginInfo pluginInfo, ExtensionAttribute extensionAttribute, string typeName)
            : this(pluginInfo, extensionAttribute)
        {
            if (string.IsNullOrEmpty(extensionAttr.Name))
                name = typeName;

            this.typeName = typeName;
        }

        protected internal ExtensionTypeInfo(PluginInfo pluginInfo, ExtensionAttribute extensionAttribute, Type type)
            : this(pluginInfo, extensionAttribute)
        {
            if (string.IsNullOrEmpty(extensionAttr.Name))
                name = type.Name;

            this.type = type;
            typeName = type.FullName;
        }

        private ExtensionTypeInfo(PluginInfo pluginInfo, ExtensionAttribute extensionAttribute)
        {
            id = nextId++;
            this.pluginInfo = pluginInfo;
            extensionAttr = extensionAttribute;
            name = extensionAttribute.Name;
            extensionInfos = new RepositoryCollection<ExtensionInfo>(new List<ExtensionInfo>());
            createExtensionHandle = InternalCreateExtension;
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public void RegisterPlugs()
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            Runtime.Composer.RegisterPlugs(this);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public void RegisterPlugs(ExtensionInfo ei)
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            Runtime.Composer.RegisterPlugs(this, ei);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public void DeregisterPlugs()
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            Runtime.Composer.DeregisterPlugs(this);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public void DeregisterPlugs(ExtensionInfo ei)
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            Runtime.Composer.DeregisterPlugs(this, ei);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public void Plug(ExtensionInfo ei)
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            Runtime.Composer.Plug(this, ei);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public ExtensionInfo GetSharedExtension()
        {
            return Runtime.Composer.GetSharedExtension(this);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public ExtensionInfo GetSharedExtension(bool createOnDemand)
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            return Runtime.Composer.GetSharedExtension(this, createOnDemand);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public ExtensionInfo GetSharedExtension(bool createOnDemand, ExtensionInfo creator)
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            return Runtime.Composer.GetSharedExtension(this, createOnDemand, creator);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public ExtensionInfo CreateSharedExtension()
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            return Runtime.Composer.CreateSharedExtension(this);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public ExtensionInfo CreateSharedExtension(AppDomain appDomain)
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            return Runtime.Composer.CreateSharedExtension(this, appDomain);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public ExtensionInfo CreateSharedExtension(ExtensionInfo creator)
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            return Runtime.Composer.CreateSharedExtension(this, creator);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public ExtensionInfo CreateSharedExtension(ExtensionInfo creator, AppDomain appDomain)
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            return Runtime.Composer.CreateSharedExtension(this, creator, appDomain);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public ExtensionInfo CreateUniqueExtension()
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            return Runtime.Composer.CreateUniqueExtension(this);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public ExtensionInfo CreateUniqueExtension(AppDomain appDomain)
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            return Runtime.Composer.CreateUniqueExtension(this, appDomain);
        }

        internal override QualificationState InternalQualify()
        {
            InternalMainQualify();
            return VerifyEventHandlerMethods(true);
        }

        public void LogEvent(LogEventArgs args)
        {
            Runtime.OnLogEvent(this, args);
        }

        internal void InternalRegisterPlugs(bool isManual, uint taskGroupId, Assembly initiator)
        {
            foreach (var extensionInfo in Runtime.Repository.ExtensionTypeInfos.
                Where(extensionTypeInfo => extensionTypeInfo.HasSlotFor(this)).
                SelectMany(extensionTypeInfo => extensionTypeInfo.extensionInfos))
                extensionInfo.InternalRegisterInSlots(this, isManual, taskGroupId, initiator);
        }

        internal void InternalDeregisterPlugs(Assembly initiator, bool force)
        {
            foreach (var extensionTypeInfo in Runtime.Repository.ExtensionTypeInfos)
                for (var i = extensionTypeInfo.extensionInfos.Count - 1; i >= 0; --i)
                    extensionTypeInfo.extensionInfos[i].InternalDeregisterFromSlots(this, initiator, force);
        }

        internal bool HasSlotFor(ExtensionTypeInfo extensionTypeInfo)
        {
            return (from plugTypeInfo in extensionTypeInfo.plugTypeInfos
                    from RepositoryElement repositoryElement in slotTypeInfos
                    where repositoryElement.Name == plugTypeInfo.Name
                    select plugTypeInfo).Any();
        }

        internal ExtensionInfo InternalCreateSharedExtension(ExtensionInfo creator, AppDomain appDomain,
            bool isManual, uint taskGroupId, Assembly initiator)
        {
            return InternalCreateSharedExtension(
                creator,
                appDomain,
                isManual,
                taskGroupId,
                initiator,
                null);
        }

        internal ExtensionInfo InternalCreateSharedExtension(ExtensionInfo creator, AppDomain appDomain,
            bool isManual, uint taskGroupId, Assembly initiator, ExtensionInfo ignorePlug)
        {
            ExtensionInfo result;

            if (sharedExtension == null)
            {
                Runtime.Invoke(
                    createExtensionHandle,
                    new object[]
                        {
                            isManual,
                            true,
                            appDomain,
                            initiator
                        });

                if (sharedExtension != null)
                    Runtime.TaskQueue.Enqueue(
                        TaskFactory.CreatePlugPlugs(
                            sharedExtension,
                            false,
                            taskGroupId,
                            Runtime.Assembly,
                            ignorePlug));

                result = sharedExtension;
            }
            else
            {
                if (isManual)
                    Runtime.OnLogEvent(
                        this,
                        new LogEventArgs(
                            MessageType.Warning,
                            VerbosityLevel.Normal,
                            Messages.ErrorOnCreateExtension,
                            new object[]
                                {
                                    id.ToString(CultureInfo.InvariantCulture),
                                    Name,
                                    string.Format("\n  {0}", Messages.SharedExtensionAlreadyExists)
                                }));
                result = null;
            }

            return result;
        }

        internal ExtensionInfo InternalCreateExtension(bool isManual, bool shared, AppDomain appDomain, Assembly initiator)
        {
            if (!IsQualified && !Qualify())
            {
                Runtime.OnLogEvent(
                    this,
                    new LogEventArgs(
                        MessageType.Warning,
                        VerbosityLevel.Normal,
                        Messages.ErrorOnCreateExtension,
                        new object[]
                        {
                            id.ToString(CultureInfo.InvariantCulture),
                            Name,
                            string.Format("\n  {0}", Messages.ExtensionTypeIsNotQualified)
                        }));

                return null;
            }

            if (extensionAttr.Singleton && extensionInfos.Count > 0)
            {
                if (isManual)
                    Runtime.OnLogEvent(
                        this,
                        new LogEventArgs(
                            MessageType.Warning,
                            VerbosityLevel.Normal,
                            Messages.ErrorOnCreateExtension,
                            new object[]
                            {
                                id.ToString(CultureInfo.InvariantCulture),
                                Name,
                                string.Format("\n  {0}", Messages.SingletonExtensionAlreadyExists)
                            }));

                return null;
            }

            if (monitorInfos.Count > 0 && extensionInfos.Count > 0)
            {
                if (isManual)
                    Runtime.OnLogEvent(
                        this,
                        new LogEventArgs(
                            MessageType.Warning,
                            VerbosityLevel.Normal,
                            Messages.ErrorOnCreateExtension,
                            new object[]
                            {
                                id.ToString(CultureInfo.InvariantCulture),
                                Name,
                                string.Format("\n  {0}", Messages.MonitorExtensionAlreadyExists)
                            }));

                return null;
            }

            var args = new CancelExtensionEventArgs(this, initiator);
            OnExtensionCreating(this, args);

            if (args.Cancel)
            {
                Runtime.OnLogEvent(
                    this,
                    new LogEventArgs(
                        MessageType.Info,
                        VerbosityLevel.Normal,
                        Messages.CreatingExtensionCanceled,
                        new object[]
                        {
                            Name
                        }));

                return null;
            }

            object obj = null;

            try
            {
                var isReflectionOnly = IsReflectionOnly;
                obj = Activator.CreateInstance(appDomain, Assembly.FullName, type.FullName).Unwrap();

                if (isReflectionOnly)
                    RuntimeBase.SetPluginAssembly(pluginInfo, obj.GetType().Assembly);
            }
            catch (Exception ex)
            {
                var exception = ex;

                while (exception.InnerException != null)
                    exception = exception.InnerException;

                Runtime.OnLogEvent(
                    this,
                    new LogEventArgs(
                        MessageType.Error,
                        VerbosityLevel.Minimal,
                        Messages.ErrorOnCreateExtension,
                        new object[]
                        {
                            id.ToString(CultureInfo.InvariantCulture),
                            Name,
                            string.Format("\n  {0}", exception.Message)
                        }));
            }

            if (obj == null)
                return null;

            var extensionInfo = new ExtensionInfo(this, obj);

            if (shared)
                sharedExtension = extensionInfo;

            extensionInfos.Add(extensionInfo);
            Runtime.Repository.objects.Add(obj, extensionInfo);
            extensionInfo.OnCreated(this, new ExtensionEventArgs(extensionInfo, initiator));

            foreach (var monitorInfo in monitorInfos)
                monitorInfo.Initialize(obj);

            return extensionInfo;
        }

        internal QualificationState InternalMainQualify()
        {
            var builder = new StringBuilder();

            if (plugTypeInfos.Count == 0)
            {
                builder.AppendFormat("\n  {0}", Messages.ExtensionHasNoPlugs);
                var extensionTypeInfo = this;
                var state = (long)(extensionTypeInfo.qualificationState | QualificationState.ExtensionHasNoPlugs);
                extensionTypeInfo.qualificationState = (QualificationState)state;
            }

            if (type.GetConstructor(new Type[0]) == null)
            {
                builder.AppendFormat("\n  {0}", Messages.ExtensionHasNoDefaultCtor);
                var extensionTypeInfo = this;
                var state = (long)(extensionTypeInfo.qualificationState | QualificationState.ExtensionNoDefaultConstructor);
                extensionTypeInfo.qualificationState = (QualificationState)state;
            }

            var status = Runtime.Repository.ExtensionTypeInfos.Any(
                extensionTypeInfo => extensionTypeInfo.Name == Name &&
                    extensionTypeInfo != this && extensionTypeInfo.IsQualified);

            if (status)
            {
                builder.AppendFormat("\n  {0}", Messages.ExtensionAlreadyExists);
                var extensionTypeInfo = this;
                var state = (long)(extensionTypeInfo.qualificationState | QualificationState.ExtensionNameAlreadyExists);
                extensionTypeInfo.qualificationState = (QualificationState)state;
            }
            else
            {
                var extensionTypeInfo = this;
                var state = (long)(extensionTypeInfo.qualificationState & ~QualificationState.ExtensionNameAlreadyExists);
                extensionTypeInfo.qualificationState = (QualificationState)state;
            }

            var extensionTypeInfo1 = this;
            var stat = (long)(extensionTypeInfo1.qualificationState & ~QualificationState.QualificationMissing);
            extensionTypeInfo1.qualificationState = (QualificationState)stat;

            RuntimeBase.LogQualifyResult(
                this,
                qualificationState,
                unqualifiedState,
                warningState,
                Messages.QualifyExtension,
                builder.ToString());

            return qualificationState;
        }

        internal QualificationState VerifyEventHandlerMethods(bool logResult)
        {
            return VerifyEventHandlerMethods(logResult, null);
        }

        internal QualificationState VerifyEventHandlerMethods(bool logResult, ICollection<ResultBase> invalidHandler)
        {
            var status = false;

            if (extensionAttr.OnCreated == string.Empty && extensionAttr.OnReleased == string.Empty)
                eventHandlerMethodInfosSet = true;
            else
            {
                if (!IsReflectionOnly && !eventHandlerMethodInfosSet)
                    status = true;

                if (extensionAttr.OnCreated != string.Empty)
                {
                    var implementingMethod = extensionAttr.OnCreated;

                    var mismatchInfo =
                        invalidHandler != null
                            ? new List
                                  <IEnumerable<HandlerParameterInfo>
                                  >()
                            : null;

                    MiOnCreated = VerifyEventHandlerMethod(
                        type,
                        extensionAttr.OnCreated,
                        Runtime.extensionEventHandlerParamTypes,
                        string.Format(
                            Messages.ExtensionEventHandlerMethodDesc,
                            "OnCreated",
                            Name), logResult,
                        IsReflectionOnly, mismatchInfo);

                    if (MiOnCreated == null)
                    {
                        var extensionTypeInfo = this;
                        var state = (long)(extensionTypeInfo.qualificationState |
                                            QualificationState.ExtensionOnCreatedMethodNotFound);

                        extensionTypeInfo.qualificationState = (QualificationState)state;

                        if (mismatchInfo != null && mismatchInfo.Count > 0)
                            mismatchInfo.ForEach(
                                hm =>
                                invalidHandler.Add(
                                    new InvalidHandlerParameter(
                                        this,
                                        "OnCreated",
                                        implementingMethod,
                                        hm)));

                        else if (mismatchInfo != null)
                            invalidHandler.Add(
                                new MissingHandler(
                                    this,
                                    "OnCreated",
                                    implementingMethod));
                    }
                    else if (status)
                        foreach (var extensionInfo in extensionInfos)
                            extensionInfo.InternalCreated +=
                                new ReflectionEventHandler(extensionInfo.Object, MiOnCreated).InvokeMethod;
                }

                if (extensionAttr.OnReleasing != string.Empty)
                {
                    var implementingMethod = extensionAttr.OnReleasing;

                    var mismatchInfo =
                        invalidHandler != null
                            ? new List
                                  <IEnumerable<HandlerParameterInfo>
                                  >()
                            : null;

                    MiOnReleasing = VerifyEventHandlerMethod(
                        type,
                        extensionAttr.OnReleasing,
                        Runtime.cancelExtensionEventHandlerParamTypes,
                        string.Format(
                            Messages.ExtensionEventHandlerMethodDesc,
                            "OnReleasing",
                            Name),
                        logResult,
                        IsReflectionOnly,
                        mismatchInfo);

                    if (MiOnReleasing == null)
                    {
                        var extensionTypeInfo = this;
                        var stat = (long)(extensionTypeInfo.qualificationState |
                                           QualificationState.ExtensionOnReleasingMethodNotFound);

                        extensionTypeInfo.qualificationState = (QualificationState)stat;

                        if (mismatchInfo != null && mismatchInfo.Count > 0)
                            mismatchInfo.ForEach(
                                hm =>
                                invalidHandler.Add(
                                    new InvalidHandlerParameter(
                                        this,
                                        "OnReleasing",
                                        implementingMethod,
                                        hm)));

                        else if (mismatchInfo != null)
                            invalidHandler.Add(
                                new MissingHandler(
                                    this,
                                    "OnReleasing",
                                    implementingMethod));
                    }
                    else if (status)
                        foreach (var extensionInfo in extensionInfos)
                            extensionInfo.Releasing +=
                                new ReflectionEventHandler(extensionInfo.Object, MiOnReleasing).InvokeMethod;
                }

                if (extensionAttr.OnReleased != string.Empty)
                {
                    var implementingMethod = extensionAttr.OnReleased;

                    var mismatchInfo =
                        invalidHandler != null
                            ? new List
                                  <IEnumerable<HandlerParameterInfo>
                                  >()
                            : null;

                    MiOnReleased = VerifyEventHandlerMethod(
                        type,
                        extensionAttr.OnReleased,
                        Runtime.extensionEventHandlerParamTypes,
                        string.Format(
                            Messages.ExtensionEventHandlerMethodDesc,
                            "OnReleased",
                            Name),
                        logResult,
                        IsReflectionOnly,
                        mismatchInfo);

                    if (MiOnReleased == null)
                    {
                        var extensionTypeInfo = this;
                        var stat = (long)(extensionTypeInfo.qualificationState |
                                           QualificationState.ExtensionOnReleasedMethodNotFound);

                        extensionTypeInfo.qualificationState = (QualificationState)stat;

                        if (mismatchInfo != null && mismatchInfo.Count > 0)
                            mismatchInfo.ForEach(
                                hm =>
                                invalidHandler.Add(
                                    new InvalidHandlerParameter(
                                        this,
                                        "OnReleased",
                                        implementingMethod,
                                        hm)));

                        else if (mismatchInfo != null)
                            invalidHandler.Add(
                                new MissingHandler(
                                    this, "OnReleased", implementingMethod));
                    }
                    else if (status)
                        foreach (var extensionInfo in extensionInfos)
                            extensionInfo.Released +=
                                new ReflectionEventHandler(extensionInfo.Object, MiOnReleased).InvokeMethod;
                }
            }

            if (status)
                eventHandlerMethodInfosSet = true;

            var eti = this;
            var stats = (long)(eti.qualificationState & ~QualificationState.MethodQualificationMissing);
            eti.qualificationState = (QualificationState)stats;

            return qualificationState;
        }

        internal void OnExtensionCreating(object sender, CancelExtensionEventArgs args)
        {
            Runtime.OnLogEvent(
                this,
                new LogEventArgs(
                    MessageType.Info,
                    VerbosityLevel.Diagnostic,
                    Messages.ExtensionCreating,
                    new object[] { Name }));

            if (ExtensionCreating == null)
                return;

            Runtime.InvokeCancelEventHandler(
                string.Format(
                    Messages.ExtensionEventHandlerMethodDesc,
                    "ExtensionCreating",
                    Name),
                ExtensionCreating,
                sender,
                args);
        }

        internal void OnExtensionCreated(object sender, ExtensionEventArgs args)
        {
            if (ExtensionCreated == null)
                return;

            Runtime.InvokeEventHandler(
                string.Format(
                    Messages.ExtensionEventHandlerMethodDesc,
                    "ExtensionCreated",
                    Name),
                ExtensionCreated,
                sender,
                args);
        }

        internal void OnExtensionReleasing(object sender, CancelExtensionEventArgs args)
        {
            if (ExtensionReleasing == null)
                return;

            Runtime.InvokeCancelEventHandler(
                string.Format(
                    Messages.ExtensionEventHandlerMethodDesc,
                    "ExtensionReleasing",
                    Name),
                ExtensionReleasing,
                sender,
                args);
        }

        internal void OnExtensionReleased(object sender, ExtensionEventArgs args)
        {
            if (ExtensionReleased == null)
                return;

            Runtime.InvokeEventHandler(
                string.Format(
                    Messages.ExtensionEventHandlerMethodDesc,
                    "ExtensionReleased",
                    Name),
                ExtensionReleased,
                sender,
                args);
        }

        internal static MethodInfo VerifyEventHandlerMethod(Type type, string methodName,
            Type[] paramTypes, string methodDescription)
        {
            return VerifyEventHandlerMethod(
                type,
                methodName,
                paramTypes,
                methodDescription,
                true,
                false,
                null);
        }

        internal static MethodInfo VerifyEventHandlerMethod(Type type, string methodName,
            Type[] paramTypes, string methodDescription, bool logResult, bool reflectionOnly,
            List<IEnumerable<HandlerParameterInfo>> mismatchInfo)
        {
            MethodInfo method = null;

            if (!reflectionOnly)
                method = type.GetMethod(methodName, paramTypes);
            else
            {
                foreach (var methodInfo in type.GetMethods())
                {
                    if (methodInfo.Name != methodName)
                        continue;

                    var parameters = methodInfo.GetParameters();
                    var status = true;

                    if (mismatchInfo == null)
                    {
                        if (parameters.Length == paramTypes.Length)
                        {
                            if (parameters.Where((t, index) =>
                                    t.ParameterType.FullName != paramTypes[index].FullName).Any())
                                status = false;
                        }
                        else
                            continue;
                    }
                    else
                    {
                        var paramInfos = new List<HandlerParameterInfo>();

                        var len = paramTypes.Length > parameters.Length
                            ? paramTypes.Length
                            : parameters.Length;

                        for (var position = 0; position < len; ++position)
                        {
                            var specified = position < paramTypes.Length
                                ? paramTypes[position]
                                : null;

                            var provided = position < parameters.Length
                                ? parameters[position].ParameterType
                                : null;

                            if (!(specified != null) || !(provided != null) ||
                                String.CompareOrdinal(specified.FullName, provided.FullName) != 0)
                            {
                                paramInfos.Add(new HandlerParameterInfo(position, specified, provided));
                                status = false;
                            }
                        }

                        mismatchInfo.Add(paramInfos);
                    }

                    if (!status)
                        continue;

                    method = methodInfo;
                    break;
                }
            }

            if (logResult)
            {
                if (method != null)
                    Runtime.OnLogEvent(
                        Runtime.Repository,
                        new LogEventArgs(
                            MessageType.Info,
                            VerbosityLevel.Diagnostic,
                            Messages.FindEventHandlerMethod,
                            new object[]
                                {
                                    methodDescription,
                                    string.Format(
                                        "\n  {0}.{1}({2} sender, {3} args)",
                                        (object) type.Name,
                                        (object) methodName,
                                        (object) paramTypes[0].Name,
                                        (object) paramTypes[1].Name),
                                    Messages.QualifyResultOk
                                }));
                else
                    Runtime.OnLogEvent(
                        Runtime.Repository,
                        new LogEventArgs(
                            MessageType.Warning,
                            VerbosityLevel.Normal,
                            Messages.FindEventHandlerMethod,
                            new object[]
                                {
                                    methodDescription,
                                    string.Format(
                                        "\n  {0}.{1}({2} sender, {3} args)",
                                        (object) type.Name,
                                        (object) methodName,
                                        (object) paramTypes[0].Name,
                                        (object) paramTypes[1].Name),
                                    Messages.QualifyResultFailed
                                }));
            }

            return method;
        }

        internal delegate ExtensionInfo CreateExtensionDelegate(bool isManual, bool shared, AppDomain appDomain, Assembly initiator);
    }
}

